package com.yfk.web.util;


import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * ** description 描述 **
 *
 * @author : duanlmz
 * @date :2022/7/20 17:51
 */
public class HttpClientUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    public static final String POST = "post";

    public static final String GET = "get";

    private static long CONNECT_TIMEOUT = 60;
    private static long READ_TIMEOUT = 60;
    private static final long WRITE_TIMEOUT = 60;

    public static void setConnectTimeOut(long connectTimeout) {
        CONNECT_TIMEOUT = connectTimeout;
        READ_TIMEOUT = connectTimeout;
    }
    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url
     *            发送请求的 URL
     * @param param
     *            请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) throws Exception {
        OutputStream out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");//解决乱码
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = conn.getOutputStream();
            out.write(param.getBytes(StandardCharsets.UTF_8));
            out.flush();
            int code = conn.getResponseCode();
//            if (code == 200) {
                in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
                String line;

                while ((line = in.readLine()) != null) {
                    result += line + "\n";
                }
//            }
        } catch (Exception e) {
            throw new Exception("http请求异常:"+e.getMessage());
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                result="请求http失败"+ex.getMessage();
                throw new Exception("http请求异常:" + result);
            }
        }
        return result;
    }

    /**
     * 通过文件地址下载附件
     * @param fileUrl   http://c/test/test.pdf
     * @param destinationFilePath 文件保存
     * @throws IOException
     */
    public static void downloadFile(String fileUrl, String destinationFilePath) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(fileUrl)
                .addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36")
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Failed to download file: " + response);
            }
            try (InputStream inputStream = response.body().byteStream();
                 FileOutputStream fileOutputStream = new FileOutputStream(destinationFilePath)) {

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, bytesRead);
                }
            }
        }
    }

    /**
     * 下载浏览器文件
     * @param fileUrl
     * @return
     * @throws IOException
     */
    public static ResponseEntity<byte[]> downloadFileInputStream(String fileUrl) throws IOException {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(fileUrl)
                .addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36")
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Failed to download file: " + response);
            }
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = response.body().byteStream().read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }

            byte[] fileBytes = bos.toByteArray();

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("filename", "example.txt");

            return new ResponseEntity<>(fileBytes, headers, HttpStatus.OK);
        } catch (IOException e) {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    public static String doGet(String url, String path, Map<String, Object> headerParams, Map<String, Object> mapParams) {
        String resultString = "";
        Response response = null;
        try {
            //******** 一、 创建 httpClient 对象***********
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                    .build();

            String params = setUrlParams(mapParams);
            String strinb = url +
                    path +
                    params;

            // ******** 二、创建request 对象*************
            Request.Builder builder = new Request.Builder()
                    .url(strinb)
                    .get();
            if (null != headerParams && headerParams.size() > 0) {
                headerParams.forEach((key, val) -> {
                    builder.addHeader(key, val.toString());
                });
            }
            Request request = builder.build();
            //********* 三、发送请求 ********************

            response = httpClient.newCall(request).execute();

            //********* 四、对响应进行处理 ***************
            //1、如果 http 状态码不在 【200 ，300】区间内，抛出异常
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }
            // 因为服务器返回的数据可能非常大，所以必须通过数据流的方式来进行访问
            // 提供了诸如 string() 和 bytes() 这样的方法将流内的数据一次性读取完毕
            resultString = response.body().string();

        } catch (SocketTimeoutException e) {
            // 超时
            throw new RuntimeException();
        } catch (IOException e) {
            // 网络IO异常
            throw new RuntimeException(e);
        } catch (Exception e) {
            // 其他异常
            throw new RuntimeException(e);
        } finally {
            if (response != null) {
                // body 必须被关闭，否则会发生资源泄漏；
                response.body().close();
            }
        }
        return resultString;
    }

    public static String doPost(String url, String path, Map<String, Object> headerParams, Map<String, Object> queryParams, Object obj) {
        Response response = null;
        String resultString = "";
        try {
            //******* 一、创建 httpClient 对象 **********
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                    .build();

            StringBuffer stringB = new StringBuffer();
            stringB.append(url);
            stringB.append(path);
            if (null != queryParams && queryParams.size() > 0) {
                String params = setUrlParams(queryParams);
                stringB.append(params);
            }

            MediaType mediaType = MediaType.parse("application/json");
//            RequestBody body = RequestBody.create(JSONUtil.parseObj(obj).toString(), mediaType);
            RequestBody body = RequestBody.create(obj.toString(), mediaType);

            //******** 二、创建 request 对象 ************
            Request.Builder builder = new Request.Builder()
                    .url(stringB.toString())
                    .post(body);
            if (null != headerParams && headerParams.size() > 0) {
                headerParams.forEach((key, val) -> {
                    builder.addHeader(key, val.toString());
                });
            }
            Request request = builder.build();
            //******** 三、执行请求 ********************
            response = httpClient.newCall(request).execute();

            //******** 四、处理响应 ********************
            int code = response.code();
            String message = response.message();
            resultString = response.body().string();
            if (!response.isSuccessful()) {
                logger.error(" Unexpect code " + response);
//                throw new IOException(" Unexpect code " + response);
            }
        } catch (SocketTimeoutException e) {
            logger.error("###### SocketTimeoutException" + e.getMessage(), e);
            // 超时
//            throw new RuntimeException();
        } catch (IOException e) {
            logger.error("###### IOException" + e.getMessage(), e);
            // 网络IO异常
//            throw new RuntimeException();
        } catch (Exception e) {
            logger.error("###### Exception" + e.getMessage(), e);
            // 其他异常
//            throw new RuntimeException();
        } finally {
            if (response != null) {
                // body 必须被关闭，否则会发生资源泄漏；
                response.body().close();
            }
        }
        return resultString;
    }

    public static String doPostText(String url, Map<String, Object> headerMap, Map<String, Object> dataMap, String bodyparams) {
        HttpRequest httpRequest = HttpRequest.post(url)
                .header("Content-Type", "text/plain")
                .header("Accept", "*/*");
        if (headerMap!=null){
            for (Map.Entry<String, Object> entry : headerMap.entrySet()) {
                httpRequest.header(entry.getKey(), entry.getValue().toString());
            }
        }
        logger.error("\n 发起请求url信息{}",url);
        logger.error("\n 请求体信息{}",bodyparams);
        //返回结果
        String result = "" ;
        try{
            result= httpRequest.body(bodyparams)
                    .timeout(30000)//超时，毫秒
                    .execute().body();
        }catch (Exception e){
            logger.error("请求失败，失败信息：" + e.getMessage(),e);
        }

        logger.error("\n 请求返回信息{}",result);
        return result;
    }

    public static String doDelete(String url, String path, Map<String, Object> headerParams, Map<String, Object> queryParams) {
        Response response = null;
        String resultString = "";
        try {
            //******* 一、创建 httpClient 对象 **********
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                    .build();

            StringBuffer stringB = new StringBuffer();
            stringB.append(url);
            stringB.append(path);
            if (null != queryParams && queryParams.size() > 0) {
                String params = setUrlParams(queryParams);
                stringB.append(params);
            }
            MediaType mediaType = MediaType.parse("application/json");

            //******** 二、创建 request 对象 ************

            Request.Builder builder = new Request.Builder()
                    .url(stringB.toString())
                    .delete();
            if (null != headerParams && headerParams.size() > 0) {
                headerParams.forEach((key, val) -> {
                    builder.addHeader(key, val.toString());
                });
            }
            Request request = builder.build();
            //******** 三、执行请求 ********************
            response = httpClient.newCall(request).execute();

            //******** 四、处理响应 ********************
            if (!response.isSuccessful()) {
                throw new IOException(" Unexpect code " + response);
            }
            resultString = response.body().string();

        } catch (SocketTimeoutException e) {
            // 超时
            throw new RuntimeException();
        } catch (IOException e) {
            // 网络IO异常
            throw new RuntimeException();
        } catch (Exception e) {
            // 其他异常
            throw new RuntimeException();
        } finally {
            if (response != null) {
                // body 必须被关闭，否则会发生资源泄漏；
                response.body().close();
            }
        }
        return resultString;
    }

    public static String doPut(String url, String path, Map<String, Object> headerParams, Map<String, Object> queryParams, Object obj) {

        Response response = null;
        String resultString = "";
        try {
            //******* 一、创建 httpClient 对象 **********
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                    .build();

            StringBuffer stringB = new StringBuffer();
            stringB.append(url);
            stringB.append(path);
            if (null != queryParams && queryParams.size() > 0) {
                String params = setUrlParams(queryParams);
                stringB.append(params);
            }
            MediaType mediaType = MediaType.parse("application/json");
            RequestBody body = RequestBody.create(JSONUtil.parseObj(obj).toString(), mediaType);

            //******** 二、创建 request 对象 ************

            Request.Builder builder = new Request.Builder()
                    .url(stringB.toString())
                    .put(body);
            if (null != headerParams && headerParams.size() > 0) {
                headerParams.forEach((key, val) -> {
                    builder.addHeader(key, val.toString());
                });
            }
            Request request = builder.build();
            //******** 三、执行请求 ********************
            response = httpClient.newCall(request).execute();

            //******** 四、处理响应 ********************
            if (!response.isSuccessful()) {
                throw new IOException(" Unexpect code " + response);
            }

            resultString = response.body().string();

        } catch (SocketTimeoutException e) {
            // 超时
            throw new RuntimeException();
        } catch (IOException e) {
            // 网络IO异常
            throw new RuntimeException();
        } catch (Exception e) {
            // 其他异常
            throw new RuntimeException();
        } finally {
            if (response != null) {
                // body 必须被关闭，否则会发生资源泄漏；
                response.body().close();
            }
        }
        return resultString;
    }

    public static String setUrlParams(Map<String, Object> mapParams) {
        // 是否已经设置了问号
        boolean hasSetQuestionMark = false;
        StringBuffer strParams = new StringBuffer();
        if (mapParams != null) {
            Iterator<String> iterator = mapParams.keySet().iterator();
            String key = "";
            while (iterator.hasNext()) {
                try {
                    key = iterator.next();
                    if (!hasSetQuestionMark) {
                        strParams.append("?").append(key).append("=")
                                .append(mapParams.get(key).toString());
                        // .append(URLEncoder.encode(mapParams.get(key).toString(), "utf-8"));
                        hasSetQuestionMark = true;
                        continue;
                    }
                    strParams.append("&").append(key).append("=")
                            .append(mapParams.get(key).toString());
                    //.append(URLEncoder.encode(mapParams.get(key).toString(), "utf-8"));
                } catch (Exception e) {
                    logger.error("参数设置异常：", e);
                }
            }
        }
        return strParams.toString();
    }

    public static String doGet(String url, Map<String, Object> headerParams, Map<String, Object> mapParams) {
        return doGet(url, "", headerParams, mapParams);
    }

    public static String doPost(String url, Map<String, Object> headerParams, Map<String, Object> queryParams, Object obj) {
        return doPost(url, "", headerParams, queryParams, obj);
    }

    public static String doDelete(String url, Map<String, Object> headerParams, Map<String, Object> queryParams) {
        return doDelete(url, "", headerParams, queryParams);
    }

    public static String doPut(String url, Map<String, Object> headerParams, Map<String, Object> queryParams, Object obj) {
        return doPut(url, "", headerParams, queryParams, obj);
    }
}
